/*
    openaltimeter -- an open-source altimeter for RC aircraft
    Copyright (C) 2010  Jan Steidl
    http://openaltimeter.org

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.openaltimeter.desktopapp.flightmodes;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Map.Entry;
import java.util.prefs.Preferences;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTable;
import javax.swing.SpringLayout;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.JTextComponent;

import org.openaltimeter.desktopapp.Controller;

public class FlightModesSetupDialog extends JDialog 
{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static final String PREFS_LAST_MODEL = "LAST_MODEL";
	private static final String PREFS_MODELS = "MODELS";

	private final class NewModelActionListener implements ActionListener 
	{		
		public void actionPerformed(ActionEvent event) {
			if (((DefaultComboBoxModel) comboBox.getModel()).getIndexOf(newModelName) < 0) {
				newModel();
			}
			comboBox.setSelectedItem(newModelName);
			comboBox.requestFocus();
			
			JTextComponent editor = (JTextComponent) comboBox.getEditor().getEditorComponent();
			editor.setSelectionStart(0);
		    editor.setSelectionEnd(newModelName.length());
		}
	}

	private final class ModelSelectionChangeListener implements ItemListener 
	{
		private String lastSelected;
		
		public void itemStateChanged(ItemEvent event) 
		{
			String newElement = event.getItem().toString();
			
			if (event.getStateChange() == ItemEvent.SELECTED) {
				if (newElement.isEmpty())
					removeModel(lastSelected);
				else 
				{
					if (flightModesHashTable.containsKey(event.getItem().toString()))
						flightModesTableModel.setData(flightModesHashTable.get(newElement).getFlightModes());
					else 
						renameModel(newElement, lastSelected);
					
					currentModeSettings = flightModesHashTable.get(newElement);
				}
				
				slider.setValue((int) (currentModeSettings.getOpacity() * 100));
			}
			else
				lastSelected = newElement;
		}
	}

	private Hashtable<String, FlightModeSetting> flightModesHashTable = new Hashtable<String, FlightModeSetting>();
	
	private final JPanel contentPanel = new JPanel();
	private JTable table;
	private JSlider slider;
	private final JComboBox comboBox;
	private DefaultComboBoxModel comboBoxModel = null;
	private FlightModesTableModel flightModesTableModel = null;
	private FlightModeSetting currentModeSettings = null;
    private Preferences prefs;
	private static String newModelName = "---New model---";
	
	/**
	 * Create the dialog.
	 */
	@SuppressWarnings("serial")
	public FlightModesSetupDialog(final Controller controller) 
	{
		this.prefs = Preferences.userNodeForPackage(this.getClass());
		
		setTitle("Flight modes");
		initData();
		
		setBounds(100, 100, 450, 360);
		getContentPane().setLayout(new BorderLayout());
		contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
		getContentPane().add(contentPanel, BorderLayout.CENTER);
		SpringLayout sl_contentPanel = new SpringLayout();
		contentPanel.setLayout(sl_contentPanel);
		
		comboBox = new JComboBox();
		sl_contentPanel.putConstraint(SpringLayout.NORTH, comboBox, 10, SpringLayout.NORTH, contentPanel);
		comboBox.setEditable(true);
		comboBox.addItemListener(new ModelSelectionChangeListener());
		comboBox.setModel(comboBoxModel);
		contentPanel.add(comboBox);
		
		JLabel lblPreset = new JLabel("Model");
		sl_contentPanel.putConstraint(SpringLayout.WEST, lblPreset, 15, SpringLayout.WEST, contentPanel);
		sl_contentPanel.putConstraint(SpringLayout.WEST, comboBox, 10, SpringLayout.EAST, lblPreset);
		lblPreset.setLabelFor(comboBox);
		sl_contentPanel.putConstraint(SpringLayout.NORTH, lblPreset, 15, SpringLayout.NORTH, contentPanel);
		contentPanel.add(lblPreset);
		
		JButton btnLoad = new JButton("");
		sl_contentPanel.putConstraint(SpringLayout.WEST, btnLoad, -40, SpringLayout.EAST, contentPanel);
		sl_contentPanel.putConstraint(SpringLayout.EAST, btnLoad, -10, SpringLayout.EAST, contentPanel);
		btnLoad.addActionListener(new NewModelActionListener());
		btnLoad.setToolTipText("Create new model settings");
		sl_contentPanel.putConstraint(SpringLayout.EAST, comboBox, -10, SpringLayout.WEST, btnLoad);
		sl_contentPanel.putConstraint(SpringLayout.NORTH, btnLoad, 9, SpringLayout.NORTH, contentPanel);
		btnLoad.setIcon(new ImageIcon(FlightModesSetupDialog.class.getResource("/toolbarButtonGraphics/general/New16.gif")));
		contentPanel.add(btnLoad);
		
		JPanel panel = new JPanel();
		sl_contentPanel.putConstraint(SpringLayout.NORTH, panel, 10, SpringLayout.SOUTH, comboBox);
		sl_contentPanel.putConstraint(SpringLayout.WEST, panel, 10, SpringLayout.WEST, contentPanel);
		sl_contentPanel.putConstraint(SpringLayout.EAST, panel, -10, SpringLayout.EAST, contentPanel);
		panel.setBorder(new TitledBorder(null, "Flight modes", TitledBorder.LEADING, TitledBorder.TOP, null, null));
		contentPanel.add(panel);
		SpringLayout sl_panel = new SpringLayout();
		panel.setLayout(sl_panel);

		JScrollPane scrollPane = new JScrollPane();
		sl_panel.putConstraint(SpringLayout.NORTH, scrollPane, 0, SpringLayout.NORTH, panel);
		sl_panel.putConstraint(SpringLayout.WEST, scrollPane, 5, SpringLayout.WEST, panel);
		sl_panel.putConstraint(SpringLayout.EAST, scrollPane, -5, SpringLayout.EAST, panel);
		sl_contentPanel.putConstraint(SpringLayout.NORTH, scrollPane, 22, SpringLayout.SOUTH, comboBox);
		sl_contentPanel.putConstraint(SpringLayout.WEST, scrollPane, 15, SpringLayout.WEST, contentPanel);
		sl_contentPanel.putConstraint(SpringLayout.SOUTH, scrollPane, -93, SpringLayout.SOUTH, contentPanel);
		sl_contentPanel.putConstraint(SpringLayout.EAST, scrollPane, -15, SpringLayout.EAST, contentPanel);
		panel.add(scrollPane);
		
		table = new JTable();
        table.setDefaultRenderer(Color.class, new ColorRenderer(true));
        table.setDefaultEditor(Color.class, new ColorEditor());
        table.setRowHeight(18);
        Dimension dimension = table.getTableHeader().getPreferredSize();
        dimension.height = 18;
        table.getTableHeader().setPreferredSize(dimension);
		table.setModel(flightModesTableModel);
        		
        Action delete = new AbstractAction()
        {
            public void actionPerformed(ActionEvent e)
            {
                JTable table = (JTable)e.getSource();
                int modelRow = Integer.valueOf( e.getActionCommand() );
                ((FlightModesTableModel)table.getModel()).removeRow(modelRow);
            }
        };
        @SuppressWarnings("unused")
		ButtonColumn buttonColumn = new ButtonColumn(table, delete, 2);

  		scrollPane.setViewportView(table);
  		
		JButton btnAddMode = new JButton("Add mode");
		btnAddMode.addActionListener(new ActionListener() 
		{
			public void actionPerformed(ActionEvent arg0) 
			{
				((FlightModesTableModel) table.getModel()).addRow();
			}
		});
		sl_panel.putConstraint(SpringLayout.SOUTH, scrollPane, -5, SpringLayout.NORTH, btnAddMode);
		sl_panel.putConstraint(SpringLayout.SOUTH, btnAddMode, -5, SpringLayout.SOUTH, panel);
		sl_panel.putConstraint(SpringLayout.EAST, btnAddMode, -5, SpringLayout.EAST, panel);
		panel.add(btnAddMode);
		
		JLabel lblOpacity = new JLabel("Opacity");
		sl_contentPanel.putConstraint(SpringLayout.WEST, lblOpacity, 15, SpringLayout.WEST, contentPanel);
		sl_contentPanel.putConstraint(SpringLayout.EAST, lblOpacity, 80, SpringLayout.WEST, contentPanel);
		contentPanel.add(lblOpacity);
		
		slider = new JSlider();
		slider.setValue((int) (currentModeSettings.getOpacity() * 100));
		slider.addChangeListener(new ChangeListener() 
		{
			public void stateChanged(ChangeEvent event) 
			{
				JSlider source = (JSlider) event.getSource();
			    if (!source.getValueIsAdjusting()) {
			    	currentModeSettings.setOpacity(source.getValue() / 100.0f);
			    }
			}
		});
		sl_contentPanel.putConstraint(SpringLayout.SOUTH, panel, -10, SpringLayout.NORTH, slider);
		sl_contentPanel.putConstraint(SpringLayout.NORTH, lblOpacity, 0, SpringLayout.NORTH, slider);
		sl_contentPanel.putConstraint(SpringLayout.WEST, slider, 0, SpringLayout.EAST, lblOpacity);
		sl_contentPanel.putConstraint(SpringLayout.SOUTH, slider, -10, SpringLayout.SOUTH, contentPanel);
		slider.setPaintLabels(true);
		slider.setMajorTickSpacing(20);
		sl_contentPanel.putConstraint(SpringLayout.EAST, slider, -5, SpringLayout.EAST, contentPanel);
		lblOpacity.setLabelFor(slider);
		contentPanel.add(slider);
				
		{
			JPanel buttonPane = new JPanel();
			buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
			getContentPane().add(buttonPane, BorderLayout.SOUTH);
			{
				JButton okButton = new JButton("OK");
				okButton.addActionListener(new ActionListener() 
				{
					public void actionPerformed(ActionEvent arg0) 
					{
						controller.doHighlightFlightModes(currentModeSettings);
						
						prefs.put(PREFS_MODELS, flightModesToString());
						prefs.put(PREFS_LAST_MODEL, currentModeSettings.getName());
						dispose();
					}
				});
				okButton.setActionCommand("OK");
				buttonPane.add(okButton);
				//getRootPane().setDefaultButton(okButton);
			}
			{
				JButton cancelButton = new JButton("Cancel");
				cancelButton.addActionListener(new ActionListener() 
				{
					public void actionPerformed(ActionEvent arg0) 
					{
						dispose();
					}
				});
				cancelButton.setActionCommand("Cancel");
				buttonPane.add(cancelButton);
			}
		}
	}
	
	private String flightModesToString() 
	{
		StringBuilder sb = new StringBuilder();
		
		for (Entry<String, FlightModeSetting> entry : flightModesHashTable.entrySet()) 
		{
			sb.append(entry.getValue().toString()).append("$");
		}
		
		return sb.toString();
	}

	private void initData() 
	{
		comboBoxModel = new DefaultComboBoxModel();
		flightModesTableModel = new FlightModesTableModel();
		flightModesHashTable = new Hashtable<String, FlightModeSetting>();
		
		String settings = prefs.get(PREFS_MODELS, "");
		String lastModel = prefs.get(PREFS_LAST_MODEL, "");
		
		try 
		{
			String models[] = settings.split("\\$");
			
			for (String model : models) 
			{
				FlightModeSetting setting = new FlightModeSetting("");
				setting.setFlightModes(model.replaceAll("\\$", ""));
				flightModesHashTable.put(setting.getName(), setting);
			}
			
			currentModeSettings = flightModesHashTable.get(lastModel);
			
			resetComboBoxItems();
			
			flightModesTableModel.setData(currentModeSettings.getFlightModes());			
			comboBoxModel.setSelectedItem(currentModeSettings.getName());
		}
		catch (Exception e)
		{
			e.printStackTrace();
			newModel();
		}
	}

	private void removeModel(String lastSelected) 
	{
		comboBoxModel.removeElement(lastSelected);
		flightModesHashTable.remove(lastSelected);
		
		// if we remove last model, create new default one
		if (flightModesHashTable.isEmpty())
			newModel();
			
		comboBox.setSelectedIndex(0);
		currentModeSettings = flightModesHashTable.get(comboBox.getSelectedItem().toString());
		flightModesTableModel.setData(currentModeSettings.getFlightModes());
		slider.setValue((int) (currentModeSettings.getOpacity() * 100));
	}

	private void renameModel(String newName, String lastSelected) 
	{
		flightModesHashTable.remove(lastSelected);
		currentModeSettings.setName(newName);
		flightModesHashTable.put(currentModeSettings.getName(), currentModeSettings);		
		
		resetComboBoxItems();
		
		comboBoxModel.setSelectedItem(currentModeSettings.getName());
		slider.setValue((int) (currentModeSettings.getOpacity() * 100));
	}

	private void resetComboBoxItems() 
	{
		ArrayList<String> models = new ArrayList<String>(flightModesHashTable.keySet());
		Collections.sort(models, String.CASE_INSENSITIVE_ORDER);
		comboBoxModel.removeAllElements();
		for (String model : models)
			comboBoxModel.addElement(model);
	}

	private void newModel() 
	{
		FlightModeSetting setting = new FlightModeSetting(newModelName);
		setting.setOpacity(0.1f);
		flightModesHashTable.put(setting.getName(), setting);
		comboBoxModel.insertElementAt(setting.getName(), 0);
		flightModesTableModel.setData(setting.getFlightModes());
		currentModeSettings = setting;
		
		comboBoxModel.setSelectedItem(currentModeSettings.getName());
		if (slider != null) 
			slider.setValue((int) (currentModeSettings.getOpacity() * 100));
	}
}
